package gim_client

import (
	"errors"
	"gim-client/utils"
	"google.golang.org/protobuf/proto"
	"io"
	"net"
	"time"
)

var (
	ClientId string
	tcpCli   *tcpClient
)

type tcpClient struct {
	Conn      net.Conn
	WriteAble bool
}

func (t *tcpClient) writeAble() error {

	if t.Conn != nil && t.WriteAble {
		return nil
	} else {
		return errors.New("连接不可用")
	}
}

const (
	AloneChat int32 = iota
	GroupChat

	// 客户端网络加入，退出
	JoinEvent = "join"
	QuitEvent = "quit"

	// 业务交互
	SendChatEvent  = "sendChat"
	JoinGroupEvent = "joinGroup"
)

type BrokerOption struct {
	Endpoint       string
	ConnectTimeOut int32
}

func (b *BrokerOption) ConnectBroker() error {

	conn, err := net.DialTimeout("tcp", b.Endpoint, time.Duration(b.ConnectTimeOut)*time.Second)
	defer conn.Close()
	if err != nil {
		Logger.Errorf("无法连接到服务端 %v", err)
		return err
	}
	tcpCli = &tcpClient{Conn: conn, WriteAble: true}
	go tcpCli.heartbeat()
	Logger.Info("连接gim-broker成功")
	err = tcpCli.join() // 建立连接成功，立马发送join消息 加入服务端
	if err != nil {
		Logger.Error("join gim-broker 失败")
		return err
	}
	readMsg(conn)
	return err
}

func readMsg(conn net.Conn) {
	for {
		command, data, err := decode(conn)
		if err != nil {
			if err == io.EOF {
				Logger.Error("TCP连接被服务端移除")
				tcpCli.WriteAble = false
			} else {
				Logger.Errorf("TCP连接断开 %v", err)
			}
			return
		}
		switch command {
		case CommandTransfer:
			err := Transfer(data)
			if err != nil {
				Logger.Error(err)
			}
			break
		}
	}
}

func Transfer(data []byte) error {

	msg := &TransferMsg{}
	err := proto.Unmarshal(data, msg)
	if err != nil {
		return err
	}
	if err := verifyTransferMsg(msg); err != nil {
		return err
	}

	switch msg.MsgType {
	case AloneChat:
		// todo 处理私聊消息
		return receiveAloneChat(msg)
	case GroupChat:
		// todo 处理群聊消息
		return receiveGroupChat(msg)
	default:
		Logger.Errorf("不匹配的消息类型 [%d]", msg.MsgType)
		return nil
	}
}

// 接收私聊
func receiveAloneChat(msg *TransferMsg) error {
	Logger.Infof("收到消息 [私聊] FromId [%s] 内容 [%s]", msg.FromId, msg.Content)
	return nil
}

// 接收群聊
func receiveGroupChat(msg *TransferMsg) error {
	Logger.Infof("收到消息 [群聊] FromId [%s] GroupId [%s] 内容 [%s]", msg.FromId, msg.GroupId, msg.Content)
	return nil
}

// 校验消息属性是否准确
func verifyTransferMsg(msg *TransferMsg) error {

	if msg.FromId == "" {
		return errors.New("消息来源Id is nil")
	}
	if msg.Content == "" {
		return errors.New("消息内容 is nil")
	}
	if msg.MsgType == GroupChat && msg.GroupId == "" {
		return errors.New("群组消息 但是 groupId is nil")
	}
	return nil
}

// 发送聊天消息
func (c *tcpClient) sendChat(msg *TransferMsg) error {

	bytes, err := proto.Marshal(msg)
	if err != nil {
		return err
	}
	if err = c.writeAble(); err != nil {
		return err
	}
	_, err = c.Conn.Write(encode(CommandTransfer, bytes))
	if err != nil {
		return err
	}
	return nil
}

func (c *tcpClient) joinGroup(msg *JoinGroupMsg) error {

	bytes, err := proto.Marshal(msg)
	if err != nil {
		return err
	}
	if err = c.writeAble(); err != nil {
		return err
	}
	_, err = c.Conn.Write(encode(CommandJoinGroup, bytes))
	if err != nil {
		return err
	}
	return nil
}

func (c *tcpClient) join() error {

	Logger.Infof("[join] ClientId [%s]", ClientId)
	msg := &JoinMsg{ClientId: ClientId}
	bytes, err := proto.Marshal(msg)
	if err != nil {
		return err
	}
	if err = c.writeAble(); err != nil {
		return err
	}
	_, err = c.Conn.Write(encode(CommandJoin, bytes))
	if err != nil {
		Logger.Error("发送消息失败 [join]")
	}
	return err
}

func (c *tcpClient) quit() error {

	msg := &JoinMsg{ClientId: ClientId}
	bytes, err := proto.Marshal(msg)
	if err != nil {
		return err
	}
	if err = c.writeAble(); err != nil {
		return err
	}
	_, err = c.Conn.Write(encode(CommandQuit, bytes))
	if err != nil {
		Logger.Error("发送消息失败 [join]")
	}
	return err
}

func (c *tcpClient) heartbeat() {

	Logger.Info("开启心跳协程")
	for {
		h := &HeartbeatMsg{ClientId: ClientId}
		bytes, err := proto.Marshal(h)
		if err != nil {
			Logger.Error("构造心跳消息失败")
			goto Sleep
		}
		if err = c.writeAble(); err != nil {
			Logger.Error("发送心跳消息[连接不可用]")
			goto Sleep
		}
		_, err = c.Conn.Write(encode(CommandHeartbeat, bytes))
		if err != nil {
			Logger.Error("发送心跳信息[ERR]")
		} else {
			Logger.Error("发送心跳信息[OK]")
		}
	Sleep:
		time.Sleep(15 * time.Second)
	}
}

func InitResource() {

	clientId, _ := utils.GenerateRandomString(10)
	Logger.Infof("[客户端身份凭证] ClientId [%s]", clientId)
	ClientId = clientId
}

func convertAndSend(event string, msg interface{}) (err error) {

	switch event {
	case JoinEvent:
		err = tcpCli.join()
		break
	case QuitEvent:
		err = tcpCli.quit()
		break
	case SendChatEvent:
		m, ok := msg.(Msg)
		if !ok {
			return errors.New("类型转换失败")
		}
		transferMsg := &TransferMsg{FromId: ClientId, ToId: m.ToId, MsgType: m.MsgType, GroupId: m.GroupId, Content: m.Content}
		err = tcpCli.sendChat(transferMsg)
		break
	case JoinGroupEvent:
		m, ok := msg.(Msg)
		if !ok {
			return errors.New("类型转换失败")
		}
		jg := &JoinGroupMsg{GroupId: m.GroupId, ClientId: ClientId}
		err = tcpCli.joinGroup(jg)
		break
	}
	return
}
